from abc import abstractmethod, ABC
from typing import Callable, Literal, Generator, Any, Sequence
from dataclasses import dataclass


def luhn(code: str) -> bool:
    """ Проверка Луна"""
    LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
    evens = sum(int(i) for i in code[-1::-2])
    odds = sum(LOOKUP[int(i)] for i in code[-2::-2])
    return ((evens + odds) % 10 == 0)


def ean13(code: str) -> bool:
    """ Проверка EAN13 """
    all_sum = sum(int(i) for i in code[1::2]) * 3 + \
        sum(int(i) for i in code[:-1:2])
    ostatok = all_sum % 10
    krat = all_sum + 10 - ostatok
    if krat - all_sum == int(code[-1]):
        return True
    elif krat - all_sum == 10 and int(code[-1]) == 0:
        return True
    else:
        return False


def your_check():
    pass


@dataclass
class Code:
    code_dip: str
    code: int
    number_count: int = 11
    """
    code_dip - уникальный номер
    code - часть от которой будет производиться генерация
    number_count - необходимое количество цифр - code_dip
    * Для ean13 используется 11 цифр
    """

    def __str__(self):
        return self.code_dip + str(self.code).zfill(self.number_count)


@dataclass
class InstructorGeneration(ABC):
    """
    - check_method - метод проверки
    - count - количество barcode
    - otstup - отступить от заданого на
    - shag - шаг по полученым code

    """
    check_method: Callable[[str], bool]
    count: int = 1
    otstup: int = 0
    shag: int = 1
    filters: Callable[[Any], bool | str] = lambda code: True

    @abstractmethod
    def __call__(self, **kwargs):
        pass


class from_luhn_in_ean13(InstructorGeneration):
    def slice_luhn(self, code: Code):
        code.code = int(str(code.code)[6:]) + 0

    def __call__(self, reform_code: Sequence[Code]) -> Generator[str, None, None]:
        """
        - reform_code - Последовательность Сode
        """
        for code in reform_code:
            self.slice_luhn(code)
            for _ in range(10):
                code.code += 1
                if ean13(code.__str__()):
                    yield code.__str__()


class generate(InstructorGeneration):
    def __call__(self, code: Code, root: int = 1) -> Generator[str, None, None]:
        """
        root - множитель который увеличает заданый Code
        """
        shag = self.shag
        while self.count > 0 and self.filters(code):
            code.code += root
            if self.check_method(code.__str__()):
                if shag == 1 and self.otstup <= 0:
                    shag = self.shag
                    self.count -= 1
                    yield code.__str__()
                elif shag > 1:
                    shag -= 1
                elif self.otstup > 0:
                    self.otstup -= 1
